//********************************************************************************************************************//
//ORGANIZATION:
//AUTHOR: bpeng(bogers.peng@gmail.com)
//SUMMARY:
//********************************************************************************************************************//
#ifndef NATIVE_SYSTEM_BASE_RANGE_H
#define NATIVE_SYSTEM_BASE_RANGE_H

#ifndef NATIVE_SYSTEM_BASE_DUAD_H
#include "System/Base/Duad.h"
#endif
#ifndef NATIVE_SYSTEM_BASE_TOSTRING_H
#include "System/Base/ToString.h"
#endif
#ifndef NATIVE_SYSTEM_BASE_EXCEPTIONS_H
#include "System/Base/Exceptions.h"
#endif

namespace Native { namespace System
{
    //****************************************************************************************************************//
    //COutOfRangeException
    //
    //****************************************************************************************************************//
    template<typename _TNumeric>
    class NATIVE_LIBRARY_EXPORT CRange :
        public CDuad<_TNumeric, _TNumeric>
    {
        NATIVE_DECLARE_OBJECT;

        NATIVE_REDEFINE(public, _TNumeric, TNumeric, T_NUMERIC);
        NATIVE_REDEFINE(public, CRange<TNumeric>, Class, CLASS);

    public:
        static const TNumeric INITIAL_VALUE = 0;

        inline static bool ValidatFromTo(T_NUMERIC& from, T_NUMERIC& to)
        {
            return from <= to;
        };

    public:
        CRange()
        {
            Delimit(INITIAL_VALUE, INITIAL_VALUE);
        };

        CRange(CLASS& that) :
            CDuad(that)
        {};

        CRange(CLASS&& that) :
            CDuad(that)
        {};

        CRange(T_NUMERIC& from, T_NUMERIC& to) throw()
        {
            try {
                Delimit(from, to);
            } catch (CException& e) {
                NATIVE_THROW_CAUGHT(L"CRange", e);
            }
        };

        virtual wstring ToString() const
        {
            wchar_t csting[CException::MAX_MESSAGE_LENGTH] = {0};
            wsprintf(
                csting,
                L"From:[%s] To:[%s]",
                UToString::FromDecimal<TNumeric>(From()).c_str(),
                UToString::FromDecimal<TNumeric>(To()).c_str());

            return csting;
        }

        TNumeric From() const
        {
            return Left();
        };

        TNumeric To() const
        {
            return Right();
        };

        void Delimit(T_NUMERIC& from, T_NUMERIC& to) throw()
        {
            auto validate = [&](wstring& message) {
                if (ValidatFromTo(from, to)) {
                    return true;
                } else {
                    wchar_t csting[CException::MAX_MESSAGE_LENGTH] = {0};

                    wsprintf(csting,
                        L"From value:[%s] exceeds To value:[%s]",
                        UToString::FromDecimal<TNumeric>(from).c_str(),
                        UToString::FromDecimal<TNumeric>(to).c_str());
                    
                    message.append(csting);
                    
                    return false;
                }
            };
            CRuntimeException::Check(NATIVE_SCENE(L"CRange"), validate, L"from, to");

            Set(from, to);
        }

        bool InOpenRange(T_NUMERIC& point) const
        {
            return point > From() && point < To();
        };

        bool InCloseRange(T_NUMERIC& point) const
        {
            return point >= From() && point <= To();
        };

        bool InLeftOpenRange(T_NUMERIC& point) const
        {
            return point > From() && point <= To();
        };

        bool InRightOpenRange(T_NUMERIC& point) const
        {
            return point >= From() && point < To();
        };

        CLASS& operator=(CLASS& rvalue)
        {
            CDuad::operator=(rvalue);

            return *this;
        }
    };
    //****************************************************************************************************************//
    //COutOfRangeException
    //
    //****************************************************************************************************************//
    template<typename _TNumeric>
    class NATIVE_LIBRARY_EXPORT COutOfRangeException :
        public CException
    {
        NATIVE_DECLARE_OBJECT;

        NATIVE_REDEFINE(public, _TNumeric, TNumeric, T_NUMERIC);
        NATIVE_REDEFINE(public, COutOfRangeException<TNumeric>, Class, CLASS);
        typedef function<bool(T_NUMERIC&, const CRange<TNumeric>&)> FValidate;

    public:
        static void Check(
            C_SCENE& scene,
            T_NUMERIC& point,
            const CRange<TNumeric>& range,
            FValidate validate,
            WSTRING& arguments = EMPTY_STRING,
            WSTRING& detail = EMPTY_STRING) throw()
        {
            if (!validate(point, range)) {
                throw COutOfRangeException<TNumeric>(scene, point, range, arguments, detail);
            }
        };

    protected:
        COutOfRangeException()
        {};
    public:
        COutOfRangeException(CLASS& that) :
            CException(that)
        {};

        COutOfRangeException(CLASS&& that) :
            CException(that)
        {};

        COutOfRangeException(
            C_SCENE& scene,
            TNumeric point,
            typename CRange<TNumeric>::CLASS& range,
            WSTRING& arguments = EMPTY_STRING,
            WSTRING& detail = EMPTY_STRING)
        {
            wchar_t message[MAX_MESSAGE_LENGTH] = {0};
            
            wsprintf(
                message,
                L"Input point:[%s] exceeds allowable arrange:{%s}",
                UToString::FromDecimal(point).c_str(),
                range.ToString().c_str());
            
            Append(CCase(scene, message, arguments, detail));
        }
    };
}}

#endif